%{open Ast %}

%token<string> ID

/* Primitive types */
%token<int> INT
%token<bool> BOOLEAN
%token<float> FLOAT
%token<string> STRING

/* Grouping symbols */
%token LPAREN RPAREN RCURL LCURL RBRACE LBRACE BAR

/* Punctuation */
%token COMMA DOT COLON ASSIGN

/* Arithmetic Operators */
%token PLUS MINUS TIMES DIVIDE MOD

/* Boolean Operators */
%token NOT AND OR

/* Inc Operators */
%token INCREMENT DECREMENT

/* Comparison Operators */
%token NEQ EQ LT GT LEQ GEQ

/* Update Operators */
%token PLUSEQ MINUSEQ TIMESEQ SLASHEQ

/* Separators */
%token EOL EOF

/* Keywords */
%token IF ELSEIF ELSE WHILE FOR RETURN RULES

/* Built in functions */
%token CAST PLAY INPUT OUTPUT

/* Type keywords */
%token INTTYPE FLOATTYPE STRINGTYPE BOOLTYPE

/* Types from AST*/
%token<Ast.comparisonOp>CompOp
%token<Ast.updateOp>UpOp
%token<Ast.incrementOp>Incdec
%token<Ast.mathOp>MathOp
%token<Ast.expr>Expression


%left NOT
%right ASSIGN
%left EQ NEQ
%left LT GT LEQ GEQ
%left PLUS MINUS
%left TIMES DIVIDE MOD

%start expr
%type <Ast.stmt> expr

%%

expr:
	LPAREN Expression RPAREN	{ Expr(ParenExpr($2)) }
|	funcCall 					{ Expr($1) }
|	binaryOperation				{ Expr($1) }
|	dotAccess 					{ Expr($1) }
|   collectionIndex				{ Expr($1) }
|   unaryNot					{ Expr($1) }
| 	unaryNeg 					{ Expr($1) }
|	comparison 					{ Expr($1) }
| 	id 							{ Expr($1) }
| 	BOOLEAN 					{ Expr(Literal(Bool($1)))}
|	INT							{ Expr(Literal(Int($1)))}
|	FLOAT						{ Expr(Literal(Float($1)))}
|   STRING 						{ Expr(Literal(String($1)))}
| 	enum						{ Expr($1) }
/*| 	struct						{ "foo"} */
| 	collection					{ Expr($1) }

stmt:
	varAssignment 			{ Literal(String("varAssignment")) }
| 	rules 					{ $1 }
| 	ifStatement 			{ $1 }
|   incrementing 		    { $1 }
| 	updatingID 				{ $1 }
| 	forLoop 				{ Literal(String("forLoop"))}
| 	whileLoop				{ Literal(String("whileLoop")) }
|   RETURN Expression 		{ Literal(String("return expr")) }


/* Blocks */

block:
	LCURL blockInterior RCURL			{ $2 }

blockInterior:
	stmt EOL							{ $1 }
| 	stmt EOL blockInterior				{ $1 }


/* Structs */
/*struct:
	LCURL structInterior RCURL 			{ $2 }
	
structInterior:
	structElement COMMA structInterior 	{ "struct element + " } 
| 	structElement						{ "struct element " } */
/*|*/ 	/* empty */							/*{ "empty struct body"}

structElement:
	id COLON Expr  						{ $1 }*/


/* Assignment */

varAssignment:
    id ASSIGN Expression     { VarAssignment($1,$3) }

funcAssignment:
	funcName ASSIGN block				{ $1 }


/* Rules statement */

rules:
	RULES LPAREN varAssignmentList RPAREN LCURL ruleStmtList RCURL {Literal(String("rules"))}

varAssignmentList:
	/* empty */								{ "empty" }
|	varAssignment 							{ "$1" }
| 	varAssignment COMMA varAssignmentList 	{ "varAssignmentList" }

ruleStmtList:
	/* empty */								{ "empty" }
| 	ruleStmt 								{ $1 }
| 	ruleStmt EOL ruleStmtList 				{ "ruleStmtList"}

ruleStmt:
	LPAREN comparison RPAREN block 			{ "rulesStmt"}


/* Functions*/
funcName:
	id LPAREN idList RPAREN   	{ FuncName($1,$3) }

funcCall:
	id LPAREN argList RPAREN 	{ FunctionCall($1,$3) }

argList:
	/* empty */					{ [Literal(String("no args"))] }
| 	Expression 					{ [$1] }
| 	Expression COMMA argList 	{ [Literal(String("argList"))] }


/* Identifiers */
id:
	ID					{ Id($1) }

/* A comma separated list of identifiers */
idList:
	/* empty */			{ [Literal(String("no params"))] }
| 	id 					{ [$1] }
|	id COMMA idList		{ [$1] } 


/* Enums */
enum:
	BAR idList BAR		{ Enum($2) }


/* Collections */
collection:
	LBRACE collectionInterior RBRACE	{Collection($2)}
| 	LBRACE /* empty */ RBRACE 			{Literal(String("empty collection"))}
|	LBRACE expr COLON expr RBRACE		{Literal(String("range"))}

collectionInterior:
	expr COMMA collectionInterior		{$3}
|	expr 								{[Literal(String("var"))]}


/* Begin Section: Unary Operators */

incrementing:
	Expression Incdec	 				{IncDec($1, $2)}

unaryNot:
	NOT Expression						{UnaryNot($2)}

unaryNeg:
	MINUS Expression 						{UnaryNeg($2)}
| 	MINUS INT 								{UnaryNegInt($2)}
/* Updating Identifiers */
updatingID:
	id UpOp Expression 					{UpdateOp($1,$3)}

/* Collection index */
collectionIndex:
	collection LBRACE Expression RBRACE 	{CollectionIndex($1,$3)}
| 	id LBRACE Expression RBRACE 			{CollectionIndex($1,$3)}

/* Dot access */	
dotAccess:
	Expression DOT STRING 			{ DotAccess($1, $3)}
| 	Expression DOT ID 				{ DotAccess($1, $3)}
	
/* End Section: Unary Operators */

/* Begin Section: Binary Operations */
binaryOperation:
	Expression MathOp Expression 				{Binop($1,$2,$3)}

/* Comparisons */

comparison:
	Expression CompOp Expression 			{Comparison($1,$2,$3)}

/* End Section: Binary Operators */

/* Control Structures */

whileLoop:
	WHILE LPAREN comparison RPAREN block			{$3}

forLoop:
	FOR LPAREN id COLON collection RPAREN block		{ForLoop($3,$5,[Expr($7)])}

ifStatement:
	IF LPAREN comparison RPAREN block				{ IfStmt($3,[$5]) }
| 	IF LPAREN comparison RPAREN ELSE block 			{ Literal(String("if with else block")) } 
| 	IF LPAREN comparison RPAREN elseif 				{ Literal(String("if with elseif")) }
| 	IF LPAREN comparison RPAREN elseif ELSE block	{ Literal(String("if with elseif and else")) }
	
elseif:
	ELSEIF LPAREN comparison RPAREN block			{ "last or only elseif" }
| 	ELSEIF LPAREN comparison RPAREN block elseif	{ "not the only elseif" }
